package com.authine.cloudpivot.ext.utils.premission;

import com.authine.cloudpivot.data.CriteriaQuery;
import com.authine.cloudpivot.data.Criterion;
import com.authine.cloudpivot.data.Quoter;
import com.authine.cloudpivot.data.criterion.*;
import com.authine.cloudpivot.data.support.SqlWithParams;
import com.authine.cloudpivot.data.support.Visits;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;

import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

public class WhereTranslator {

    private final String tablePrefix;

    private final IndexHolder holder = new IndexHolder();

    private final Map<String, Object> parameters = new TreeMap<>();

    public WhereTranslator(String tablePrefix) {
        this.tablePrefix = tablePrefix;
    }

    public SqlWithParams translate(CriteriaQuery criteriaQuery) {
        return new SqlWithParams(translate(criteriaQuery.getCriterion()), parameters);
    }

    private String translate(Blob blob) {
        return translate(new SimpleExpression(blob.field, blob.op, blob.value));
    }

    private String translate(SimpleExpression expression) {
        String v = "NULL";
        if (expression.value != null) {
            v = getPlaceholders();
            parameters.put(getParameterName(), expression.value);
        }
        if (expression.op.equals(Op.IN) || expression.op.equals(Op.NOT_IN)) {
            return String.format(" %s %s (%s)", quote(expression.field), Visits.visit(expression.op, expression.value), v);
        }
        return String.format(" %s %s %s", quote(expression.field), Visits.visit(expression.op, expression.value), v);
    }

    private String quote(String field) {
        return tablePrefix == null ? "`" + field + "`" :  tablePrefix + ".`" + field + "`";
    }

    private String translate(final Conjunction conjunction) {
        return conjunction.conditions().stream().map(it -> "(" + translate(it) + ")")
                .collect(Collectors.joining(" AND "));
    }

    private String translate(Disjunction disjunction) {
        return disjunction.conditions().stream().map(it -> "(" + translate(it) + ")")
                .collect(Collectors.joining(" OR "));
    }

    private String translate(Criterion criterion) {
        if (criterion instanceof Conjunction) {
            return translate((Conjunction) criterion);
        }
        if (criterion instanceof Disjunction) {
            return translate((Disjunction) criterion);
        }
        if (criterion instanceof Blob) {
            return translate((Blob) criterion);
        }
        if (criterion instanceof SimpleExpression) {
            return translate((SimpleExpression) criterion);
        }
        return "";
    }

    private String getPlaceholders() {
        return String.format(":param%d", holder.incrementAndGet());
    }

    private String getParameterName() {
        return String.format("param%d", holder.getIndex());
    }

    private void putParameter(String parameterName, Object value) {
        parameters.put(parameterName, value);
    }

    @Getter
    @Setter
    private static class IndexHolder {

        int index = 0;

        int incrementAndGet() {
            return ++index;
        }
    }
}
